package weatherstation;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.Socket;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import databases.MongoDB;
import objects.Measurement;
import utilities.ANSI;
import utilities.FormatDouble;

/**
 * The Class WeatherStationWorker.
 * 
 * @author Nils Berlijn
 * @author Henderikus Harms
 * @version 1.0
 * @since June 1, 2014
 */
public class WeatherStationWorker implements Runnable 
{
	/** The weather station socket. */
	private Socket weatherStationSocket;
	
	/** The weather station worker socket. */
	private InetAddress weatherStationWorkerSocket;
	
	/** The reader. */
	private BufferedReader reader;
    
    /** The input. */
    private String input;
    
    /** The measurement. */
    private Measurement measurement;

	/**
	 * Instantiates a new weather station worker.
	 *
	 * @param weatherStationSocket the weather station socket
	 */
	public WeatherStationWorker(Socket weatherStationSocket)
	{
		try
		{
			this.weatherStationSocket = weatherStationSocket;
			this.weatherStationWorkerSocket = weatherStationSocket.getInetAddress();
			this.reader = new BufferedReader(new InputStreamReader(this.weatherStationSocket.getInputStream()));
		}
		catch(IOException e)
		{
			
		} 
	}
	
	/* (non-Javadoc)
	 * @see java.lang.Runnable#run()
	 */
	@Override
	public void run()
	{
        try 
        {
            System.out.println(ANSI.BLUE + "Weather Station Worker: " + ANSI.GREEN + "Executing \"" + Thread.currentThread().getName() + "\"" + ANSI.NORMAL);
            
            readAndParseInput();          
            
            System.out.println(ANSI.BLUE + "Weather Station Worker: " + ANSI.GREEN + "Finished \"" + Thread.currentThread().getName() + "\"" + ANSI.NORMAL);			
            
            weatherStationSocket.close();
			
            System.out.println(ANSI.BLUE + "Weather Station Worker: " + ANSI.YELLOW + "Disconnecting \"" + weatherStationWorkerSocket() + "\"" + ANSI.NORMAL);			
        }      
		catch(IOException e)
		{
			
		}        
	}	
	
	/**
	 * Weather station worker socket.
	 *
	 * @return the inet address
	 */
	public InetAddress weatherStationWorkerSocket()
	{
		return weatherStationWorkerSocket;	
	}	
	
	/**
	 * Read and parse input.
	 */
	private void readAndParseInput()
	{
		try
		{
			measurement = new Measurement();
			
			while((input = reader.readLine()) != null)
	        {     
	        	if(input.endsWith("<MEASUREMENT>"))
	        	{
	        		measurement = new Measurement();
	        	}
	        	else if(input.endsWith("</MEASUREMENT>")) 
	        	{
	        		MongoDB.insertMeasurement(measurement);
	        	}
	        	else
	        	{
	        		Pattern regex = Pattern.compile("<(\\w+)>(.*?)<(/\\w+)>");
	        		Matcher matcher = regex.matcher(input);
	
	        		if(matcher.find()) 
	        		{
	        			String key = matcher.group(1);
	        			String value = matcher.group(2);
	        			
	        			validateMeasuerement(key, value);
	        		}   
	        	}               	 
	        }
		}
		catch(IOException e)
		{
			
		}  
	}
	
	/**
	 * Validate measuerement.
	 *
	 * @param key the key
	 * @param value the value
	 */
	private void validateMeasuerement(String key, String value)
	{
		if(value.equals(""))
			repairMeasurement(key, value);
		else if(key.equals("TEMP"))
			validateDoubleValueMeasurement(key, value, -9999.9, 9999.9);
		else if(key.equals("DEWP"))
			validateDoubleValueMeasurement(key, value, -9999.9, 9999.9);
		else if(key.equals("STP"))
			validateDoubleValueMeasurement(key, value, 0.0, 9999.9);
		else if(key.equals("SLP"))
			validateDoubleValueMeasurement(key, value, 0.0, 9999.9);
		else if(key.equals("VISIB"))
			validateDoubleValueMeasurement(key, value, 0.0, 999.9);	
		else if(key.equals("WDSP"))
			validateDoubleValueMeasurement(key, value, 0.0, 999.9);	
		else if(key.equals("PRCP"))
			validateDoubleValueMeasurement(key, value, 0.00, 999.99);
		else if(key.equals("SNDP"))
			validateDoubleValueMeasurement(key, value, -9999.9, 9999.9);
		else if(key.equals("CLDC"))
			validateDoubleValueMeasurement(key, value, 87.4, 99.99);
		else if(key.equals("WNDDIR"))
			validateIntegerValueMeasurement(key, value, 0, 359);			
		else
			insertToMeasurement(key, value);	
	}
	
	/**
	 * Validate integer value measurement.
	 *
	 * @param key the key
	 * @param value the value
	 * @param beginRange the begin range
	 * @param endRange the end range
	 */
	private void validateIntegerValueMeasurement(String key, String value, int beginRange, int endRange)
	{
		Integer data = Integer.parseInt(value);

		if(data < beginRange || data > endRange)
			repairMeasurement(key, value);
		else
			insertToMeasurement(key, value);		
	}	
	
	/**
	 * Validate double value measurement.
	 *
	 * @param key the key
	 * @param value the value
	 * @param beginRange the begin range
	 * @param endRange the end range
	 */
	private void validateDoubleValueMeasurement(String key, String value, double beginRange, double endRange)
	{
		Double data = Double.parseDouble(value);
		
		if(data < beginRange || data > endRange)
			repairMeasurement(key, value);
		else
			insertToMeasurement(key, value);
	}

	/**
	 * Repair measurement.
	 *
	 * @param key the key
	 * @param value the value
	 */
	private void repairMeasurement(String key, String value)
	{
		int stn = measurement.getStn();
		int amount = 30;
		String average;
		
		/** Integer repair */
		if(key.equals("STN") || key.equals("WNDDIR"))
		{
			average = MongoDB.selectIntegerAvarageLatestMeasurementsKeyByStnAndKey(stn, key, amount);
			value = average;
		}
		
		/** Double repair */
		else if(key.equals("TEMP") || key.equals("DEWP") || key.equals("STP") || 
				key.equals("SLP") || key.equals("VISIB") || key.equals("WDSP") || 
				key.equals("PRCP") || key.equals("SNDP") || key.equals("CLDC"))
		{
			int decimals = 1;
			
			if(key.equals("PRCP"))
				decimals = 2;

			average = MongoDB.selectDoubleAvarageLatestMeasurementsKeyByStnAndKey(stn, key, decimals, amount);
			value = average;
		}	
		
		/** String repair */
		else if(key.equals("DATE") || key.equals("TIME") || key.equals("FRSHTT"))
		{		
			value = "";
		}		

		insertToMeasurement(key, value);
	}
	
	/**
	 * Insert to measurement.
	 *
	 * @param key the key
	 * @param value the value
	 */
	private void insertToMeasurement(String key, String value)
	{
		if(key.equals("STN"))
			measurement.setStn(Integer.parseInt(value));
		else if(key.equals("DATE"))
			measurement.setDate(value);
		else if(key.equals("TIME"))
			measurement.setTime(value);
		else if(key.equals("TEMP"))
			measurement.setTemp(Double.parseDouble(FormatDouble.format(Double.parseDouble(value), 1)));	
		else if(key.equals("DEWP"))
			measurement.setDewp(Double.parseDouble(FormatDouble.format(Double.parseDouble(value), 1)));	
		else if(key.equals("STP"))
			measurement.setStp(Double.parseDouble(FormatDouble.format(Double.parseDouble(value), 1)));	
		else if(key.equals("SLP"))
			measurement.setSlp(Double.parseDouble(FormatDouble.format(Double.parseDouble(value), 1)));	
		else if(key.equals("VISIB"))
			measurement.setVisib(Double.parseDouble(FormatDouble.format(Double.parseDouble(value), 1)));	
		else if(key.equals("WDSP"))
			measurement.setWdsp(Double.parseDouble(FormatDouble.format(Double.parseDouble(value), 1)));	
		else if(key.equals("PRCP"))
			measurement.setPrcp(Double.parseDouble(FormatDouble.format(Double.parseDouble(value), 2)));	
		else if(key.equals("SNDP"))
			measurement.setSndp(Double.parseDouble(FormatDouble.format(Double.parseDouble(value), 1)));	
		else if(key.equals("FRSHTT"))
			measurement.setFrshtt(value);	
		else if(key.equals("CLDC"))
			measurement.setCldc(Double.parseDouble(FormatDouble.format(Double.parseDouble(value), 1)));	
		else if(key.equals("WNDDIR"))
			measurement.setWnddir(Integer.parseInt(FormatDouble.format(Double.parseDouble(value), 1)));			
	}
}